import { Link } from '@brillout/docpress'
import { UiFrameworkExtension } from '../../components'

To add `<head>` tags, such as `<title>` and `<meta name="description">`, you can use:

- The page setting <Link href="#head">`Head`</Link>.
- The page settings <Link href="#title-favicon">`title` & `favicon`</Link>.
- :construction: The <Link href="#usehead">`useHead()`</Link> component hook.

> These options are implemented by the <UiFrameworkExtension />. See <Link href="#without-vike-react-vue-solid" /> if you don't use such extension.


## `Head`

To add `<head>` tags to all your pages:

```jsx
// /pages/+Head.jsx or
// /pages/+Head.vue
// Environment: server

export { Head }

import socialImage from '../assets/images/socialImage.jpg'
import favicon from '../assets/images/favicon.png'

function Head() {
  return <>
    <meta charset="utf-8">
    <title>SpaceX</title>
    <meta name="description" content="We deliver payload to space.">
    <meta property="og:image" content={socialImage}>
    <link rel="icon" href={favicon}>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </>
}
```

Example: [React](https://github.com/vikejs/vike-react/blob/main/examples/full/pages/%2Bconfig.ts) / [Vue](https://github.com/vikejs/vike-vue/blob/main/examples/full/pages/%2Bconfig.ts) / [Solid](https://github.com/vikejs/vike-solid/blob/main/examples/basic/pages/%2BHead.tsx).

Setting `<head>` tags only to one page and based on <Link href="/data-fetching">fetched data</Link>:

```js
// /pages/rocket/starship/+data.js
// Environment: server

export { data }

async function data() {
  const rocket = await sql.run(
    'SELECT { title, description, social_image } FROM rockets WHERE name = "starship";'
  )
  return rocket
}
```
```jsx
// /pages/rocket/starship/+Head.jsx
// Environment: server

export { Head }

import { useData } from 'vike-react/useData' // or 'vike-vue' / 'vike-solid'

function Head() {
  const rocket = useData()
  return <>
    <meta name="description" content={rocket.description}>
    <meta property="og:image" content={rocket.social_image}>
  </>
}
```


## `title` & `favicon`

If your pages can have different `<title>` values, then use the setting `title` instead of the <Link href="#head">`Head`</Link> setting:

```js
// /pages/rocket/starship/+title.js
// Environment: server, client
export const title = 'SpaceX | Rocket Starship'
```
```js
// /pages/rocket/falcon-9/+title.js
// Environment: server, client
export const title = 'SpaceX | Rocket Falcon 9'
```

Upon <Link href="/client-routing">client-side navigating</Link> from the URL `/rocket/starship` to `/rocket/falcon-9`, the page's title is dynamically updated from `SpaceX | Rocket Starship` to `SpaceX | Rocket Falcon 9`. (By mutating the DOM with [`document.title = 'SpaceX | Rocket Falcon 9'`](https://developer.mozilla.org/en-US/docs/Web/API/Document/title).)

While defining `<title>` by using the `Head` setting correctly sets the page's title for the first page the user visits, it doesn't update the page's title upon <Link href="/client-routing">client-side navigation</Link>.

> Most `<head>` tags, such as [the social image tag `<meta property="og:image">`](https://ogp.me/), don't need to be updated upon client-side navigation because they are only used by crawlers. (Browsers don't do anything with `<meta property="og:image">`.) Crawlers don't execute JavaScript and, therefore, don't client-side navigate.

Likewise, if your [favicon](https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML#adding_custom_icons_to_your_site) can change from page to page, then define `/pages/rocket/starship/+favicon.png` instead of using the `Head` setting.

You can set `title` and `favicon` based on <Link href="/data-fetching">fetched data</Link>, for example:

```js
// /pages/rocket/starship/+title.js
// Environment: server, client

export function title(pageContext) {
  const rocket = pageContext.data
  return rocket.title
}
```
```tsx
// With TypeScript:

import type { Data } from './+data'
import type { PageContext } from 'vike/types'
export function title(pageContext: PageContext<Data>) {
  const rocket = pageContext.data
  return rocket.title
}
```
> As shown [here](https://gist.github.com/brillout/e7fe603bd44c9ec1be7e98f3ae28636a) you can define the logic, of using `data.title` to set the page's title, once and apply it to all your pages.


## `useHead()`

The upcoming `useHead()` component hook will enable any UI component to add `<head>` tags, see: [New component hook `useHead()`](https://github.com/vikejs/vike-react/issues/67).


## Internationalization

Example of internationalizing (i18n) `<head>` tags:

```jsx
// /pages/+Head.js
// Environment: server

export { Head }

import { usePageContext } from 'vike-react/usePageContext' // or 'vike-vue' / 'vike-solid'

function Head() {
  const pageContext = usePageContext()
  const description = pageContext.locale === 'de-DE' ?
    'Wir liefern zum Weltall.' :
    'We deliver payload to space.'
  return <>
    <meta name="description" content={description}>
  </>
}
```
```js
// /pages/+title.js
// Environment: server, client

export function title(pageContext) {
  const title = pageContext.locale === 'de-DE' ?
    'SpaceX | Das Weltall Unternehmen' :
    'SpaceX | The space company'
  return title
}
```

See also:
 - <Link href="/i18n" />
 - <Link href="/usePageContext" />


## Without `vike-{react,vue,solid}`

If you don't use a Vike extension, then you have direct control over `<head>` tags in your <Link href="/onRenderHtml">`onRenderHtml()` hook</Link>.

```js
// /renderer/+onRenderHtml.js
// Environment: server

import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import { renderToHtml } from 'some-ui-framework'

export { onRenderHtml }

async function onRenderHtml(pageContext) {
  return escapeInject`<html>
    <head>
      <title>SpaceX</title>
      <meta name="description" content="We deliver payload to space.">
    </head>
    <body>
      <div id="root">
        ${dangerouslySkipEscape(await renderToHtml(pageContext.Page))}
      </div>
    </body>
  </html>`
}
```


### By page

To define `<head>` tags on page-by-page basis, we recommend creating new settings, for example `title` and `description` as shown at <Link href="/meta#example-title-and-description" doNotInferSectionTitle />.


### By component

To define `<head>` tags by any UI component:
 1. Add `'headProps'` to [`passToClient`](/passToClient).
 2. Use <Link href="/usePageContext">`usePageContext()`</Link> so that `pageContext.headProps` can be accessed and modified by any component.

For example:

```js
// /renderer/+onRenderHtml.js
// Environment: server

export { onRenderHtml }

import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import renderToHtml from 'some-ui-framework'

async function onRenderHtml(pageContext) {
  // We use our UI framework to pass `pageContext.headProps` to all components
  // of our component tree. (E.g. React Context or Vue's `app.config.globalProperties`.)
  const pageHtml = await renderToHtml(
    <ContextProvider headProps={pageContext.headProps} >
      <Page />
    </ContextProvider>
  )

  // 1. One of our UI component modified pageContext.headProps while rendering components.
  // 2. We now render `headProps` to HTML meta tags.
  return escapeInject`<html>
    <head>
      <title>${pageContext.headProps.title}</title>
      <meta name="description" content="${pageContext.headProps.description}">
    </head>
    <body>
      <div id="app">
        ${dangerouslySkipEscape(pageHtml)}
      </div>
    </body>
  </html>`
}
```

```js
// SomeComponent.js

  // Inside a UI component
  const pageContext = usePageContext()
  const { headProps } = pageContext
  headProps.title = 'I was set by some component.'
  headProps.description = 'Me too.'
```


### Client Routing

If you use Client Routing, make sure to update `document.title` upon page navigation:

```js
// /renderer/+config.js
// Environment: config

export default {
  // Make pageContext.headProps available on the client-side.
  passToClient: ['headProps']
}
```

```js
// /renderer/+onRenderClient.js
// Environment: client

export { onRenderClient }

async function onRenderClient(pageContext) {
  if (!pageContext.isHydration) {
    // Client-side navigation — we update the page's title
    document.title = pageContext.headProps.title
  }
  // ...
}
```


### Libraries

You can also use libraries such as [@vueuse/head](https://github.com/vueuse/head) or [react-helmet](https://github.com/nfl/react-helmet).

But we recommend using such library only if you have a rationale as the aforementioned solutions are simpler.

Head libraries already sanitize the HTML `<head>`, this means you can skip `escapeInject` and wrap the overall result with `dangerouslySkipEscape()`.

```js
// /renderer/+onRenderHtml.js
// Environment: server

export { onRenderHtml }

import { dangerouslySkipEscape } from 'vike/server'
import { renderToHtml } from 'some-ui-framework'

async function onRenderHtml(pageContext) {
  return dangerouslySkipEscape(await renderToHtml(pageContext.Page))
}
```


## See also

- <Link href="/markdown#metadata" />
- <Link href="/data-fetching" />
- <Link href="/lang" />
- <Link href="/usePageContext" />
- <Link href="/useData" />
- <Link href="/pageContext" />
